package utils;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;

import lombok.extern.slf4j.Slf4j;

/**
 * @author jiangfeng on 2022/6/30
 */
public class TestGuava {

    Logger log  = LoggerFactory.getLogger(TestGuava.class);

    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    AtomicInteger i = new AtomicInteger();
    AtomicInteger i2 = new AtomicInteger();

    private LoadingCache<String, Integer> allTranslationLocalCache;
    private LoadingCache<String, Integer> allTranslationLocalCache2;

    @PostConstruct
    private void init() {
        allTranslationLocalCache = CacheBuilder.newBuilder()
                .refreshAfterWrite(5, TimeUnit.SECONDS)
                .build(
                        new CacheLoader<String, Integer>() {

                            @Override
                            public Integer load(String key) {
                                log.info("load reload缓存:" + key);
                                return refreshLocalTranslationCache(key);
                            }

                            @Override
                            public ListenableFuture<Integer> reload(final String key,
                                    final Integer oldValue) {
                                log.info("allContractLocalCache 异步reload缓存:{},oldValue:{}", key, oldValue);
                                ListenableFutureTask<Integer> task =
                                        ListenableFutureTask.create(() -> refreshLocalTranslationCache(key));
                                executor.execute(task);
                                return task;
                            }
                        }
                );

        allTranslationLocalCache2 = CacheBuilder.newBuilder()
                .refreshAfterWrite(5, TimeUnit.SECONDS)
                .build(
                        new CacheLoader<String, Integer>() {

                            @Override
                            public Integer load(String key) {
                                log.info("2 load reload缓存:" + key);
                                return refreshLocalTranslationCache(key);
                            }
                        }
                );
        allTranslationLocalCache.refresh("1");
        allTranslationLocalCache2.refresh("1");
    }

    private Integer refreshLocalTranslationCache(String key) {
        LocalDateTime now = LocalDateTime.now();
        log.info("local cache load,key -> {},time -> {},value:{}", key, now, i);
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        i.incrementAndGet();
        log.info("local cache load,key -> {},time -> {}", key, LocalDateTime.now());
        return i.get();
    }

    public Result<Integer> test1(String keyword) {
        Integer provences = null;
        try {
            provences = allTranslationLocalCache.get(keyword);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return Result.of(provences);
    }

    public Result<Integer> test2(String keyword) {
        Integer provences = null;
        allTranslationLocalCache.refresh(keyword);
        return Result.of(provences);
    }

    public Result<Integer> test3(String keyword) {
        Integer provences = null;
        try {
            provences = allTranslationLocalCache2.get(keyword);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return Result.of(provences);
    }

    public Result<Integer> test4(String keyword) {
        Integer provences = null;
        allTranslationLocalCache2.refresh(keyword);
        return Result.of(provences);
    }


    static class Result<T> implements Serializable {

        private static final long serialVersionUID = 1L;

        private boolean success;

        private int code;

        private String message;


        private T result;

        public static Result<Boolean> buildSuccess() {
            Result<Boolean> result = new Result<>();
            result.setSuccess(true);
            result.setResult(true);
            return result;
        }


        public static Result<Boolean> buildFailure(int errCode, String errMessage) {
            Result<Boolean> result = new Result<>();
            result.setSuccess(false);
            result.setResult(false);
            result.setCode(errCode);
            result.setMessage(errMessage);
            return result;
        }

        public static <T> Result<T> failure(int errCode, String errMessage) {
            Result<T> result = new Result<>();
            result.setSuccess(false);
            result.setCode(errCode);
            result.setMessage(errMessage);
            return result;
        }

        public static <T> Result<T> of(T data) {
            Result<T> response = new Result<>();
            response.setSuccess(true);
            response.setResult(data);
            return response;
        }

        public static <T> Result<T> of(T data, String message) {
            Result<T> response = new Result<>();
            response.setSuccess(true);
            response.setResult(data);
            response.setMessage(message);
            return response;
        }

        public T getResult() {
            return result;
        }

        public void setResult(T result) {
            this.result = result;
        }

        public boolean isSuccess() {
            return success;
        }

        public void setSuccess(boolean success) {
            this.success = success;
        }

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        @Override
        public String toString() {
            return "Response [success=" + success + ", errCode=" + code + ", errMessage=" + message + "]";
        }
    }
}